#include <assert.h>
#include <gfx/idevice.h>
#include <gfx/iprogram.h>
#include <gfx/iprogramparameter.h>
#include <gfx/mesh.h>
#include <gfx/programparametername.h>
#include <gfx/gl20/mesh/instancedmeshsw.h>
#include <GL/glew.h>


ceInstancedMeshSWGL20::ceInstancedMeshSWGL20()
{
  CE_OBJECT_CONSTR
}

void ceInstancedMeshSWGL20::BindInstanceData(iProgram* program, unsigned idx)
{
  for (unsigned i=0, j=_instanceData.size (); i<j; i++)
    {
      ceInstancedData& d = _instanceData[i];
      iProgramParameter* pp = program->GetParameter(d.Binding);
      if (!pp)
        {
          continue;
        }
      if (!d.Data)
        {
          // may not yet be initialised. so this is ok but must not be rendered.
          return;
        }

      switch (d.Type)
        {
        case IDT_Float:
          pp->Bind (d.Float[idx]);
          break;
        case IDT_Vector2:
          pp->Bind (d.Vec2[idx]);
          break;
        case IDT_Vector3:
          pp->Bind (d.Vec3[idx]);
          break;
        case IDT_Vector4:
          pp->Bind (d.Vec4[idx]);
          break;
        }
    }

}

void ceInstancedMeshSWGL20::Draw(const ceRenderEnvironment&, iDevice* device)
{
  if (_instanceUse <= 0)
    {
      return;
    }
  iProgram* program = device->GetProgram();
  assert (program);

  device->SetStreamSource(0, _mesh->GetVertices());
  device->SetIndices(_mesh->GetIndices());
  device->SetVertexDeclaration(_mesh->GetVertexDeclration());

  glDisable(GL_CULL_FACE);

  switch (_instanceUse)
    {
    case 0:
      return;
    case 1:
      BindInstanceData(program, 0);
      device->DrawIndexPrimitives(PT_Triangles, _mesh->GetIndexDataType(), _mesh->GetNumberOfPrims(), true);
      break;
    case 2:
      BindInstanceData(program, 0);
      device->DrawIndexPrimitives(PT_Triangles, _mesh->GetIndexDataType(), _mesh->GetNumberOfPrims(), false);
      BindInstanceData(program, 1);
      device->RedrawIndexPrimitives(true);
      break;

    default:
      BindInstanceData(program, 0);
      device->DrawIndexPrimitives(PT_Triangles, _mesh->GetIndexDataType(), _mesh->GetNumberOfPrims(), false);
      unsigned n = _instanceUse-1;
      for (unsigned i=1; i<n; i++)
        {
          BindInstanceData(program, i);
          device->RedrawIndexPrimitives(false);
        }
      BindInstanceData(program, n);
      device->RedrawIndexPrimitives(true);
      break;
    }

  glEnable(GL_CULL_FACE);
}
